package com.godme.concurrent.pattern;

import java.util.concurrent.atomic.AtomicInteger;

public class ReadWriteLockPattern {


    static class ReadWriteLock {
        private int readingThreads = 0;
        private int waitingWriters = 0;
        private int writingWriters = 0;
        private boolean preferWriter = true;

        // 读写互斥
        public synchronized void readLock() throws InterruptedException {
            // 存在写或者等待写，读阻塞
            while (writingWriters > 0 || (preferWriter && waitingWriters > 0)){
                wait();
            }
            readingThreads++;
        }

        public synchronized void readUnlock(){
            // 直接释放
            readingThreads --;
            // 写优先
            preferWriter = true;
            notifyAll();
        }

        public synchronized void writeLock() throws InterruptedException {
            waitingWriters ++;
            try{
                while (readingThreads > 0 || writingWriters > 0){
                    wait();
                }
            } finally {
                waitingWriters --;
            }
            writingWriters ++;
        }
        public synchronized void writeUnLock(){
            // 释放写
            writingWriters --;
            //
            preferWriter = false;
            notifyAll();
        }
    }

    static class Data {
        private final int[] arr;
        private final ReadWriteLock lock = new ReadWriteLock();
        private final int size;

        public Data(int size){
            arr = new int[size];
            this.size = size;
        }

        public int read(int index) throws InterruptedException {
            lock.readLock();
            try{
                return arr[index];
            }finally {
                lock.readUnlock();
            }
        }

        public void write(int index, int value) throws InterruptedException{
            lock.writeLock();
            arr[index] = value;
            lock.writeUnLock();
        }
    }
    
    
    static class ReadThread extends Thread{
        private final Data data;
        private final AtomicInteger readerIndex;
        
        public ReadThread(String name,AtomicInteger readerIndex, Data data){
            super(name);
            this.data = data;
            this.readerIndex = readerIndex;
        }

        @Override
        public void run() {
            try{
                while (readerIndex.get() < 100){
                    System.err.println("\t" + getName() + " : " + data.read( readerIndex.getAndIncrement() % data.size));
                    sleep(500);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    static class WriteThread extends Thread {
        private final Data data;
        private final AtomicInteger writerIndex;


        public WriteThread (String name, AtomicInteger counter ,Data data){
            super(name);
            this.writerIndex = counter;
            this.data = data;
        }

        @Override
        public void run() {
            int i;
           try{
              while (writerIndex.get() < 100){
                  i = writerIndex.getAndIncrement();
                  data.write(i % data.size, i);
                  System.err.println(getName() + " : " + i);
                  sleep(300);
              }
           }catch (Exception e){
               e.printStackTrace();
           }
        }
    }


    public static void main(String[] args) {
        Data data = new Data(20);
        AtomicInteger readerIndex = new AtomicInteger();
        AtomicInteger writerIndex = new AtomicInteger();
        for (int i = 0; i < 2; i++) {
            new WriteThread("writer-"+i, writerIndex, data).start();
        }

        for (int i = 0; i < 2; i++) {
            new ReadThread("reader-"+i, readerIndex, data).start();
        }
    }
}
